package communication;
import gnu.io.CommPort;
import gnu.io.CommPortIdentifier;
import gnu.io.SerialPort;
import gnu.io.SerialPortEventListener;
import gnu.io.SerialPortEvent;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.LinkedList;
import java.util.Observable;

import javax.swing.JProgressBar;


public class TwoWaySerialComm extends Observable
{
	private ArrayList<String> ports;
	private LinkedList<String> log;
	private final int MAX_GREY = 255;
	private final int INTSPERMESSAGE = 4;
	private int res;
	private int expected;
	private int[] input;
	private int totalRecv;
	private CommPort commPort = null;
	private SerialPort serialPort = null;
    private InputStream in = null;
    private OutputStream out = null;
    private SerialReader reader = null;
    private SerialWriter writer = null;
    private File outFile;
    private FileWriter outFileWriter;
    private StringBuffer tmpOut;
    private int numbersWritten;
	
	private long startTime;
	
    public TwoWaySerialComm()
    {
        super();
        log = new LinkedList<String>();
        ports = new ArrayList<String>();
        tmpOut = new StringBuffer();
        searchPorts();
    }
    
    public void searchPorts() {
		Enumeration enumComm;
		CommPortIdentifier serialPortId;
		
		ports.clear();
		enumComm = CommPortIdentifier.getPortIdentifiers();
		while (enumComm.hasMoreElements()) {
			serialPortId = (CommPortIdentifier) enumComm.nextElement();
			if(serialPortId.getPortType() == CommPortIdentifier.PORT_SERIAL) {
			ports.add(serialPortId.getName());
		}
	}
    }
    
    public void disconnect() throws Exception
    {
    	if(in != null) {
    		in.close();
    	}
    	if(out != null) {
    		out.close();
    	}
    	if(serialPort != null) {
    		serialPort.close();
    	}
    	if(commPort != null) {
    		commPort.close();
    	}
    	if(reader != null) {
    		reader.interrupt();
    	}
    	reader = null;
    	writer = null;
    	
    	writeLog("Disconnected");
    }
    
    public void connect ( String portName ) throws Exception
    {
        CommPortIdentifier portIdentifier = CommPortIdentifier.getPortIdentifier(portName);
        if ( portIdentifier.isCurrentlyOwned() )
        {
        	writeLog("Error: Port is currently in use");
        }
        else
        {
            commPort = portIdentifier.open(this.getClass().getName(),2000);
            if ( commPort instanceof SerialPort )
            {
                serialPort = (SerialPort) commPort;
                serialPort.setSerialPortParams(115200,SerialPort.DATABITS_8,SerialPort.STOPBITS_1,SerialPort.PARITY_NONE);
                in = serialPort.getInputStream();
                out = serialPort.getOutputStream();
                serialPort.addEventListener(new SerialPortEventListener() {
					
					@Override
					public void serialEvent(SerialPortEvent arg0) {
						readEventListener(arg0);
						
					}
				});
                serialPort.notifyOnDataAvailable(true);
//                serialPort.
                reader = new SerialReader(in);
                writer = new SerialWriter(out);
                writeLog("Connected to " + portName);
            }
            else
            {
            	writeLog("Error: Only serial ports are handled by this example.");
            }
        }      
    }
    
    public void start(int cbIndex) {
    	startTime = System.currentTimeMillis();
		switch(cbIndex) {
			case 0:
				res = 64;
				break;
			case 1:
				res = 128;
				break;
			case 2:
				res = 256;
				break;
			case 3:
				res = 512;
				break;
		}
		expected = (res*res)-((res*res)%INTSPERMESSAGE);
    	totalRecv = 0;
    	numbersWritten = 0;
    	log.clear();
    	tmpOut = new StringBuffer();
    	outFile = new File("output.pgm");
    	outFile.delete();
      	try {
      		outFile.createNewFile();
  			outFileWriter = new FileWriter(outFile, true);
  			outFileWriter.append("P2\n");
			outFileWriter.append(res + " " + res + "\n");
			outFileWriter.append(MAX_GREY + "\n");
		} catch (IOException e) {
			e.printStackTrace();
		}
    	this.send("/1" + cbIndex + "000000*");
    	writeLog("Started: Expecting " + expected + " values");
    }
    
    private void closeFile() {
    	try {
    		outFileWriter.append(tmpOut);
    		outFileWriter.close();
    	} catch(IOException e) {
    		e.printStackTrace();
    	}
}
    
    
    public void send(String toSend) {
    	writer.send(toSend);
    }

    
    public ArrayList<String> getPorts() {
    	return ports;
    }
    
    private void readEventListener(SerialPortEvent arg0) {
    	if (arg0.getEventType() == SerialPortEvent.DATA_AVAILABLE) {
	    	input = reader.read();
	    	if((input[0] != (int)'/') || (input[9] != (int)'*')) {
	    		writeLog("Ungültige Eingabe");
	    		this.stop();
	    	}
	    	else {
	    		input = interprete(input);
	    		writeFile(input);
				setChanged();
				notifyObservers();
	    	}
    	}
    }
    
    private void writeLog(String message) {
    	log.addLast(message);
    	setChanged();
    	notifyObservers();
    }
    
    public String getLogMessage() {
    	if(log.isEmpty()) {
    		return null;
    	}
    	else {
    		return log.removeFirst();
    	}
    }
    
    public boolean logMessageAvailabe() {
    	return !log.isEmpty();
    }
    
    
    private void writeFile(int[] towrite)
    {
    	totalRecv += towrite.length;
    	for (int i = 0; i < towrite.length; i++) {
    		tmpOut.append(towrite[i] + " ");
    		numbersWritten++;
    		if(numbersWritten >= 10) {
    			tmpOut.append("\n");
    			numbersWritten = 0;
    		}
    	}
    	if(isFinished()) {
    		this.stop();
    	}
    }
    
    public boolean isFinished() {
    	return totalRecv >= expected;
    }
    
    public void stop() {
    	this.send("/00000000*");
    	this.closeFile();
		long endTime = System.currentTimeMillis();
		setChanged();
		notifyObservers();
		String tmp;
		if(isFinished()) {
			tmp = "Finished: ";
		} else {
			tmp = "Incomplete: ";
		}
			writeLog(tmp + "Read " + totalRecv + " values in " + (endTime - startTime) + " ms.");
    }
    
    private int[] interprete(int[] toInterprete) {
    	int tmp1 = ((toInterprete[1]<<8)|toInterprete[2]);
    	int tmp2 = ((toInterprete[3]<<8)|toInterprete[4]);
    	int tmp3 = ((toInterprete[5]<<8)|toInterprete[6]);
    	int tmp4 = ((toInterprete[7]<<8)|toInterprete[8]);
    	int[] result = {tmp1, tmp2, tmp3, tmp4};
    	return result;
    }
    
    public int getProgress() {
    	return (int)((double)totalRecv/expected*100);
    }
}
